import Dep, { popTarget, pushTarget } from "./dep"

let id = 0
// 创建渲染watcher的时候把当前的渲染watcher放到Dep.target上
// 调用_render()会取值，走到get上

// 每个属性有一个dep(属性就是被观察者)，watcher就是观察者（属性变化会通知观察者来更新） -》观察者模式

class Watcher { // 不同组件有不同的watcher,目前只有一个渲染根实例的
    constructor(vm, exprOrFn, options, cb) {
        // debugger
        this.id = id++
        this.renderWatcher = options//true

        if (typeof exprOrFn === 'string') {
            this.getter = function () {
                return vm[exprOrFn]
            }

        } else {
            this.getter = exprOrFn//更新函数

        }

        this.deps = []//计算清理时使用
        this.depsId = new Set()
        this.lazy = options.lazy
        this.cb = cb
        this.dirty = this.lazy
        this.vm = vm
        this.user=options.user//表示是否是用户自己的watcher
        this.value= this.lazy ? undefined : this.get()
        // this.get()//往Dep.target上放watcher实例并更新
    }
    addDep(dep) {//重复属性也不用记录
        let id = dep.id
        if (!this.depsId.has(id)) {
            this.deps.push(dep)
            // debugger
            this.depsId.add(id)//Set的add方法
            dep.addSub(this)// watcher记住了dep而且去重了，此时让dep也记住watcher
        }
    }
    evaluate() {
        // console.log('evalueate')
        this.value = this.get()//获取用户函数的返回值，还要标识为脏
        this.dirty = false
    }
    get() {
        // console.log('watch',this.getter)
        // Dep.target = this//静态属性只有一份
        pushTarget(this)
        let value = this.getter.call(this.vm)//更新时会去vm上取值
        // Dep.target = null//渲染完毕后清空
        popTarget()
        return value
    }
    depend() {
        let i = this.deps.length
        while (i--) {
            this.deps[i].depend()//让计算属性watcher也收集渲染watcher
        }
    }
    update() {
        // 如果是计算属性，依赖的值变化了，就标识计算属性是脏值了
        if (this.lazy) {
            this.dirty = true
        } else {
            queueWatcher(this) // 把当前的watcher暂存起来
        }

    }
    run() {
       
        // console.log('run')
        let oldValue=this.value
        let newValue=this.get() // 渲染的时候用的是最新的vm来渲染
        if (this.user) {
            this.cb.call(this.vm, newValue,oldValue)
        }                       
    }
}

// 
let queue = []//存放去重后的watcher.id
let has = {}//去重
let pending = false//节流

// 
function flushSchedulerQueue() {
    let flushQueue = queue.slice(0)
    queue = []
    has = {}
    pending = false// 刷新时新的watcher重新放到queue中
    flushQueue.forEach(q => q.run())//更新
}
// 更新起点
// 存放多个去重后的watcher一次更新
function queueWatcher(watcher) {
    const id = watcher.id
    if (!has[id]) {
        queue.push(watcher)
        has[id] = true
        // 一轮刷新 节流
        if (!pending) {
            pending = true
            nextTick(flushSchedulerQueue, 0);//回调放入数组中异步执行更新
        }
    }
}
// 
let callbacks = []
let waiting = false
//按照顺序依次执行callbacks中的回调
function flushCallbacks() {
    let cbs = callbacks.slice(0)
    waiting = false
    callbacks = []
    cbs.forEach(cb => cb())//按照顺序依次执行
}

// nextTick没有直接使用某个api,而是采用优雅降级的方式
// 内部先采用的是promise(ie不兼容)， MutationObserver(h5的api),可以考虑下ie专属的setImmediate,setTimeout
let timerFunc;
if (Promise) {
    timerFunc = () => {
        Promise.resolve().then(flushCallbacks)
    }
} else if (MutationObserver) {
    let observer = new MutationObserver(flushCallbacks)//这里传入的回调是异步执行的
    let textNode = document.createTextNode(1)
    observer.observe(textNode, {
        characterData: true
    })
    timerFunc = () => {
        textNode.textContent = 2
    }
} else if (setImmediate) {
    timerFunc = () => {
        setTimeout(flushCallbacks);
    }
} else {
    timerFunc = () => {
        setTimeout(flushCallbacks);
    }
}
// 维护nextTick中的callback方法，异步去执行
export function nextTick(cb) {
    callbacks.push(cb)// 维护nextTick中的callbacks方法
    // debugger
    if (!waiting) {
        waiting = true
        timerFunc()//异步执行callbacks中的回调函数
    }
}

// 需要给每个属性增加一个dep,目的是收集watcher
export default Watcher